package com.xinsoft.aop.aspect;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xinsoft.aop.ano.SysLog;
import com.xinsoft.common.GlobalStatusEnum;
import com.xinsoft.constant.Const;
import com.xinsoft.entity.po.SysDept;
import com.xinsoft.entity.po.SysOperLog;
import com.xinsoft.service.SysDeptService;
import com.xinsoft.service.SysOperLogService;
import com.xinsoft.utils.*;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;

/**
 * @Author Li9527
 * @Description 系统日志切面
 * @Date 2021/2/24 11:48
 * @Param
 * @return
 **/
@Aspect
@Component
public class SysLogAspect {

    private static final Logger log = LoggerFactory.getLogger(SysLogAspect.class);

    @Autowired
    private SysOperLogService sysLogService;

    @Autowired
    private SysDeptService sysDeptService;

    /**
     * 这里我们使用注解的形式
     * 当然，我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     */
    @Pointcut("@annotation(com.xinsoft.aop.ano.SysLog)")
    public void logPointCut() {
    }

    public static ExecutorService create() {
        return ThreadUtil.newSingleExecutor();
    }

    public SysOperLog getBaseLog() {
        SysOperLog sysOperLog = new SysOperLog();
        // 请求的地址
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        sysOperLog.setOperIp(ip);
        sysOperLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
        sysOperLog.setOperLocation(AddressUtils.getRealAddressByIP(sysOperLog.getOperIp()));
        // 设置请求方式
        sysOperLog.setRequestMethod(ServletUtils.getRequest().getMethod());
        // 获取当前的用户
        SysUser sysUser = TokenUtil.getUser();
        if (sysUser != null) {
            sysOperLog.setOperUid(sysUser.getId());
            sysOperLog.setOperName(sysUser.getRealName());
            SysDept dept = sysDeptService.getById(sysUser.getDeptId());
            if (dept != null) {
                sysOperLog.setDeptName(dept.getDeptName());
            }else{
                sysOperLog.setDeptName("该人员未配置部门");
            }
        } else {
            sysOperLog.setOperUid(Const.ADMIN_UID);
            sysOperLog.setOperName("无法解析token用户");
            sysOperLog.setDeptName("无法解析部门");
        }
        return sysOperLog;
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        ExecutorService executor = create();
        SysOperLog sysOperLog = getBaseLog();
        executor.execute(() -> handleLog(joinPoint, null, jsonResult, sysOperLog));
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        ExecutorService executor = create();
        SysOperLog sysOperLog = getBaseLog();
        executor.execute(() -> handleLog(joinPoint, e, null, sysOperLog));
    }

    /**
     * @return void
     * @Author Li9527
     * @Description 保存日志
     * @Date 2021/2/24 11:36
     * @Param [joinPoint, e, jsonResult]
     **/
    private void handleLog(JoinPoint joinPoint, final Exception e, Object jsonResult, SysOperLog operLog) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();

            Method method = signature.getMethod();
            SysOperLog sysOperLog = new SysOperLog();
            BeanUtil.copyProperties(operLog, sysOperLog);
            sysOperLog.setOperTime(new Date());
            SysLog sysLog = method.getAnnotation(SysLog.class);
            // 设置action动作
            sysOperLog.setBusinessType(sysLog.businessType().ordinal());
            // 设置标题
            sysOperLog.setTitle(sysLog.title());

            //请求的 类名、方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            sysOperLog.setMethod(className + "." + methodName + "()");

            sysOperLog.setJsonResult(JSON.toJSONString(jsonResult));
            //请求的参数
            Object[] args = joinPoint.getArgs();
            try {
                List<String> list = new ArrayList<String>();
                for (Object o : args) {
                    if (!(o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof MultipartFile)) {
                        list.add(JSONObject.toJSONString(o));
                    }
                }
                sysOperLog.setOperParam(list.toString());
            } catch (Exception exp) {
                // 记录本地异常日志
                log.error("==前置通知异常==");
                log.error("异常信息:{}", exp.getMessage());
                exp.printStackTrace();
            }
            if (e != null) {
                sysOperLog.setStatus(GlobalStatusEnum.STATUS_ERROR.ordinal());
                sysOperLog.setErrorMsg(StrUtil.sub(e.getMessage(), 0, 2000));
            }
            sysLogService.save(sysOperLog);
        } catch (Exception e2) {
            log.error(e2.getMessage());
            e2.printStackTrace();
        }
    }
}
